package com.jie.service.impl;


import com.jie.constants.Constant;
import com.jie.dto.UploadImageDTO;
import com.jie.enums.FileType;
import com.jie.exception.BizException;
import com.jie.po.MultipartUploadCreatePO;
import com.jie.po.rep.FileUploadResponse;
import com.jie.po.rep.MultipartUploadCreateResponse;
import com.jie.po.req.CompleteMultipartUploadRequest;
import com.jie.po.req.MultipartUploadCreateRequest;
import com.jie.utils.FileUtil;
import com.jie.utils.ImageUtil;
import com.jie.utils.MinioUtil;
import com.jie.utils.UserUtils;
import io.minio.CreateMultipartUploadResponse;
import io.minio.ListPartsResponse;
import io.minio.ObjectWriteResponse;
import io.minio.RemoveObjectArgs;
import io.minio.messages.Part;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Objects;

import static com.jie.enums.FileType.*;

/**
 * 通过校验文件MD5实现重复文件秒传
 * 文件上传服务
 *
 * @author Blue
 * @date 2022/10/28
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class FileService {
    private final MinioUtil minioUtil;
    @Value("${upload.minio.endpoint}")
    private String minIoServer;
    @Value("${upload.minio.bucketName}")
    private String bucketName;
    @Value("${upload.minio.imagePath}")
    private String imagePath;
    @Value("${upload.minio.filePath}")
    private String filePath;
    @Value("${upload.minio.videoPath}")
    private String videoPath;


    @PostConstruct
    public void init() {
        if (!minioUtil.bucketExists(bucketName)) {
            // 创建bucket
            minioUtil.makeBucket(bucketName);
            // 公开bucket
            minioUtil.setBucketPublic(bucketName);
        }
    }
    /**
     * 创建分片上传
     * @param createRequest
     * @return
     */
    public MultipartUploadCreateResponse createMultipartUpload(MultipartUploadCreateRequest createRequest) {
        log.info("创建分片上传开始, createRequest: [{}]", createRequest);
        MultipartUploadCreateResponse response = new MultipartUploadCreateResponse();
        response.setChunks(new LinkedList<>());

        //分片上传需要的参数
        final MultipartUploadCreatePO uploadCreate = MultipartUploadCreatePO.builder()
                .bucketName(bucketName)
                .objectName(createRequest.getFileName())
                .build();
        final CreateMultipartUploadResponse uploadId = minioUtil.uploadId(uploadCreate);
        uploadCreate.setUploadId(uploadId.result().uploadId());
        response.setUploadId(uploadCreate.getUploadId());
        Map<String, String> reqParams = new HashMap<>();
        reqParams.put("uploadId", uploadId.result().uploadId());
        for (int i = 1; i <= createRequest.getChunkSize(); i++) {
            reqParams.put("partNumber", String.valueOf(i));
            String presignedObjectUrl = minioUtil.getPresignedObjectUrl(uploadCreate.getBucketName(), uploadCreate.getObjectName(), reqParams);
            MultipartUploadCreateResponse.UploadCreateItem item = new MultipartUploadCreateResponse.UploadCreateItem();
            item.setPartNumber(i);
            item.setUploadUrl(presignedObjectUrl);
            response.getChunks().add(item);
        }
        log.info("创建分片上传结束, createRequest: [{}]", createRequest);
        return response;
    }

    /**
     * 分片合并
     * @param uploadRequest
     */
    public FileUploadResponse completeMultipartUpload(CompleteMultipartUploadRequest uploadRequest) {
        log.info("文件合并开始, uploadRequest: [{}]", uploadRequest);
        try {
            final ListPartsResponse listMultipart = minioUtil.listMultipart(MultipartUploadCreatePO.builder()
                    .bucketName(bucketName)
                    .objectName(uploadRequest.getFileName())
                    .maxParts(uploadRequest.getChunkSize() + 10)
                    .uploadId(uploadRequest.getUploadId())
                    .partNumberMarker(0)
                    .build());
            final ObjectWriteResponse objectWriteResponse = minioUtil.completeMultipartUpload(MultipartUploadCreatePO.builder()
                    .bucketName(bucketName)
                    .uploadId(uploadRequest.getUploadId())
                    .objectName(uploadRequest.getFileName())
                    .parts(listMultipart.result().partList().toArray(new Part[]{}))
                    .build());

            return FileUploadResponse.builder()
                    .uploadName(uploadRequest.getFileName())
                    .size(uploadRequest.getFileSize())
                    .url( minIoServer+ "/" + bucketName + "/" + uploadRequest.getFileName())
                    .build();
        } catch (Exception e) {
            log.error("合并分片失败", e);
        }
        log.info("文件合并结束, uploadRequest: [{}]", uploadRequest);
        return null;
    }


    /**
     * 上传视频
     * @param file
     * @return
     */
    public String uploadVideo(MultipartFile file) {
        String username = UserUtils.getLoginUser().getUsername();
        if (file.getSize() > Constant.MAX_FILE_SIZE) {
            throw new BizException( "视频大小不能超过100M");
        }


        return "";

    }
    /**
     * minio上传支持文件
     * @param file
     * @return
     */
    public String uploadFile(MultipartFile file) {
        String username = UserUtils.getLoginUser().getUsername();
        // 大小校验
        if (file.getSize() > Constant.MAX_FILE_SIZE) {
            throw new BizException( "文件大小不能超过100M");
        }
        // 上传
        String fileName = minioUtil.upload(bucketName, username+"/"+filePath, file);
        if (StringUtils.isEmpty(fileName)) {
            throw new BizException( "文件上传失败");
        }
        String url = generUrl(FILE, fileName);
        log.info("文件文件成功，用户id:{},url:{}", username, url);
        return url;
    }


    /**
     * 上传图片
     * @param file
     * @param
     * @return
     */
    public UploadImageDTO uploadImage(MultipartFile file) {
        try {
            String username = UserUtils.getLoginUser().getUsername();
            // 大小校验
            if (file.getSize() > Constant.MAX_IMAGE_SIZE) {
                throw new BizException( "图片大小不能超过5M");
            }
            // 图片格式校验
            if (!FileUtil.isImage(file.getOriginalFilename())) {
                throw new BizException( "图片格式不合法");
            }
            // 上传原图
            UploadImageDTO vo = new UploadImageDTO();
            String fileName = minioUtil.upload(bucketName, username+"/"+imagePath, file);
            if (StringUtils.isEmpty(fileName)) {
                throw new BizException( "图片上传失败");
            }
            vo.setOriginUrl(generUrl(FileType.IMAGE, fileName));
            // 大于30K的文件需上传缩略图
            if (file.getSize() > 30 * 1024) {
                byte[] imageByte = ImageUtil.compressForScale(file.getBytes(), 30);
                fileName = minioUtil.upload(bucketName, username+"/"+imagePath, Objects.requireNonNull(file.getOriginalFilename()), imageByte, file.getContentType());
                if (StringUtils.isEmpty(fileName)) {
                    throw new BizException("图片上传失败");
                }
            }
            vo.setThumbUrl(generUrl(FileType.IMAGE, fileName));
            log.info("文件图片成功，用户名:{},url:{}", username, vo.getOriginUrl());
            return vo;
        } catch (IOException e) {
            log.error("上传图片失败，{}", e.getMessage(), e);
            throw new BizException("图片上传失败");
        }
    }


    /**
     * 获取当前对象的全部资源
     * @param fileTypeEnum
     * @param fileName
     * @return
     */
    public String generUrl(FileType fileTypeEnum, String fileName) {
        String username = UserUtils.getLoginUser().getUsername();
        String url = minIoServer + "/" + bucketName+"/"+username;
        switch (fileTypeEnum) {
            case FILE:
                url += "/" + filePath + "/";
                break;
            case IMAGE:
                url += "/" + imagePath + "/";
                break;
            case VIDEO:
                url += "/" + videoPath + "/";
                break;
            default:
                break;
        }
        url += fileName;
        return url;
    }

    /**
     * 删除文件
     * @param url
     * @return
     */
    public boolean removeFile(String url,String fileName) {
        try {
            minioUtil.remove(bucketName,url,fileName);
            log.info("文件删除成功，url:{}", url+fileName);
            return true;
        } catch (Exception e) {
            throw new BizException("Minio文件删除失败");
        }
    }

}
